/*
 * rs485.cpp
 *
 *  Created on: Dec 9, 2011
 *      Author: kylintse
 */

#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <unistd.h>  /* UNIX standard function definitions */
#include <fcntl.h>   /* File control definitions */
#include <errno.h>   /* Error number definitions */
#include <termios.h> /* POSIX terminal control definitions */
#include <linux/serial.h>
#include <sys/ioctl.h>

///////////////////////////////////////////////////////////////////////////////
// rs485

#define TIOCGRS485      0x542E
#define TIOCSRS485      0x542F

#include "rs485.h"
#include "gtype.h"
#include "gdata.h"

int speed_arr[] = {B115200, B38400, B19200, B9600, B4800, B2400, B1200, B600, B300};
int name_arr[] = {115200,38400,  19200,  9600,  4800,  2400,  1200, 600,  300 };

rs485::rs485(const QString& devNode)
	: m_devNode(devNode)
{
	m_fd=-1;
}

rs485::~rs485()
{
	if(m_fd!=-1)
		::close(m_fd);
}

bool rs485::init()
{
	int status;
	ParamSerial* parm;
	if(m_devNode=="/dev/ttyS1")
		parm=gSerialParm;
	else if(m_devNode=="/dev/ttyS2")
		parm=gSerialParm+1;
	else if(m_devNode=="/dev/ttyS3")
		parm=gSerialParm+2;
	else if(m_devNode=="/dev/ttyS4")
		parm=gSerialParm+3;
	else
		return false;

	m_bitrate=parm->bitrate.toInt();
	m_databits=parm->charbits.toInt();
	m_stopbits=parm->stopbits.toInt();
	m_parity=parm->parity;

	m_fd=::open(m_devNode.toAscii().data(),O_RDWR|O_NOCTTY|O_NDELAY);
	if(m_fd==-1)
		return false;

	fcntl(m_fd, F_SETFL, 0);

	struct termios   Opt;
	tcgetattr(m_fd, &Opt);
	for (int i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
	{
		if  (m_bitrate == name_arr[i])
		{
			tcflush(m_fd, TCIOFLUSH);
			cfsetispeed(&Opt, speed_arr[i]);
			cfsetospeed(&Opt, speed_arr[i]);
			status = tcsetattr(m_fd, TCSANOW, &Opt);
			if  (status != 0)
			{
				return false;
			}
			tcflush(m_fd,TCIOFLUSH);
			break;
		}
	}

	tcgetattr(m_fd, &Opt);

	Opt.c_cflag &= ~CSIZE;
	switch (m_databits) /*设置数据位数*/
	{
		case 5:
			Opt.c_cflag |= CS5;
			break;
		case 6:
			Opt.c_cflag |= CS6;
			break;
		case 7:
			Opt.c_cflag |= CS7;
			break;
		case 8:
			Opt.c_cflag |= CS8;
			break;
		default:
			return false;
	}

	switch (m_parity)
	{
		case 0:
			Opt.c_cflag &= ~PARENB;
			Opt.c_iflag &= ~INPCK;
			break;
		case 1:
			Opt.c_cflag |= (PARODD | PARENB);
			Opt.c_iflag |= INPCK;
			break;
		case 2:
			Opt.c_cflag |= PARENB;
			Opt.c_cflag &= ~PARODD;
			Opt.c_iflag |= INPCK;
			break;

		default:
			return false;
	}

	switch (m_stopbits)
	{
		case 1:
			Opt.c_cflag &= ~CSTOPB;
			break;
		case 2:
			Opt.c_cflag |= CSTOPB;
		   break;
		default:
			 return false;
	}

	Opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
	Opt.c_iflag &= ~(ICRNL|IXON | IXOFF | IXANY);
	Opt.c_oflag &= ~OPOST;

	tcflush(m_fd,TCIFLUSH);
	if (tcsetattr(m_fd,TCSANOW,&Opt) != 0)
	{
		return false;
	}

	struct serial_rs485 rs485conf;

	rs485conf.flags |= SER_RS485_ENABLED;

	if (::ioctl (m_fd, TIOCSRS485, &rs485conf) < 0)
	{
		return false;
	}

	return true;
}

int rs485::send(char* data,int size)
{
	return ::write(m_fd,data,size);
}

int rs485::recv(char* data,int size,int timeout)
{
	fd_set readfd;
	struct timeval tv;
	qint32 ret;
	int recvLen=0;

	while(recvLen<size)
	{
		tv.tv_sec=timeout/1000;
		tv.tv_usec=timeout%1000;

		FD_ZERO(&readfd);
		FD_SET(m_fd,&readfd);
		ret=::select(m_fd+1,&readfd,NULL,NULL,&tv);

		switch(ret)
		{
			case -1:
			case 0:
				return recvLen;

			default:
			{
				recvLen+=::read(m_fd,data+recvLen,size-recvLen);
				break;
			}
		}
	}

	return recvLen;
}

///////////////////////////////////////////////////////////////////////////////
rs485TestThread::rs485TestThread(rs485Test* test,QObject* parent)
	: kthread(parent),m_test(test)
{

}

rs485TestThread::~rs485TestThread()
{

}

void rs485TestThread::run()
{
	setStarted(true);
	while(true)
	{
		if(needExit())
			break;

		m_test->doTest();
	}

	setStarted(false);
}

///////////////////////////////////////////////////////////////////////////////
// rs485Test
rs485Test::rs485Test(quint32 id,const QString& devNode)
	: DeviceProtocol(id,devNode,NULL)
{
	m_port=NULL;
	m_thread=NULL;
}

rs485Test::~rs485Test()
{

}

bool rs485Test::start()
{
	m_port=new rs485(m_devNode);

	if(!m_port->init())
	{
		delete m_port;
		m_port=NULL;
		return false;
	}

	m_thread=new rs485TestThread(this);
	if(!m_thread->start(5000))
	{
		delete m_port;
		m_port=NULL;
		delete m_thread;
		m_thread=NULL;
		return false;
	}

	return true;
}

void rs485Test::stop()
{
	if(m_port)
	{
		m_thread->exit();
		m_thread->wait();
		delete m_thread;
		m_thread=NULL;

		delete m_port;
		m_port=NULL;
	}
}

void rs485Test::recycle()
{

}

void rs485Test::doTest()
{
	int recvLen;
	recvLen=m_port->recv(m_recvBuf,256,5000);

	if(recvLen!=256)
		return;

	for(int i=0;i<256;i++)
	{
		if(m_recvBuf[i]!=i)
			return;
	}
	m_port->send(m_recvBuf,256);

	/*for(int i=0;i<16;i++)
		m_recvBuf[i]=i;

	m_port->send(m_recvBuf,16);*/
}
